﻿using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Media;
using Telerik.Windows.Documents.Fixed.Model.Graphics;
using ArcSegment = Telerik.Windows.Documents.Fixed.Model.Graphics.ArcSegment;
using BezierSegment = Telerik.Windows.Documents.Fixed.Model.Graphics.BezierSegment;
using FillRule = Telerik.Windows.Documents.Fixed.Model.Graphics.FillRule;
using LineSegment = Telerik.Windows.Documents.Fixed.Model.Graphics.LineSegment;
using PathFigure = Telerik.Windows.Documents.Fixed.Model.Graphics.PathFigure;
using PathGeometry = Telerik.Windows.Documents.Fixed.Model.Graphics.PathGeometry;
using PathSegment = System.Windows.Media.PathSegment;
using QuadraticBezierSegment = Telerik.Windows.Documents.Fixed.Model.Graphics.QuadraticBezierSegment;
using RectangleGeometry = Telerik.Windows.Documents.Fixed.Model.Graphics.RectangleGeometry;
using SweepDirection = Telerik.Windows.Documents.Fixed.Model.Graphics.SweepDirection;

namespace Com.Lancetinc.Evs.Reports.RadPdf;

internal static class PdfGeometryHelper
{
    private static readonly
        Dictionary<Type, Func<PathSegment, IEnumerable<Telerik.Windows.Documents.Fixed.Model.Graphics.PathSegment>>>
        segmentConverters;

    static PdfGeometryHelper()
    {
        segmentConverters =
            new Dictionary<Type,
                Func<PathSegment, IEnumerable<Telerik.Windows.Documents.Fixed.Model.Graphics.PathSegment>>>();
        segmentConverters.Add(typeof(System.Windows.Media.ArcSegment),
            path => ToArcSegments((System.Windows.Media.ArcSegment)path));
        segmentConverters.Add(typeof(System.Windows.Media.BezierSegment),
            path => ToBezierSegments((System.Windows.Media.BezierSegment)path));
        segmentConverters.Add(typeof(PolyBezierSegment), path => ConvertPolyBezierSegments((PolyBezierSegment)path));
        segmentConverters.Add(typeof(System.Windows.Media.LineSegment),
            path => ToLineSegments((System.Windows.Media.LineSegment)path));
        segmentConverters.Add(typeof(PolyLineSegment), path => ConvertPolyLineSegment((PolyLineSegment)path));
        segmentConverters.Add(typeof(System.Windows.Media.QuadraticBezierSegment),
            path => ToQuadraticBezierSegments((System.Windows.Media.QuadraticBezierSegment)path));
        segmentConverters.Add(typeof(PolyQuadraticBezierSegment),
            path => ConvertPolyQuadraticBezierSegment((PolyQuadraticBezierSegment)path));
    }

    public static FillRule ConvertFillRule(System.Windows.Media.FillRule fillRule)
    {
        switch (fillRule)
        {
            case System.Windows.Media.FillRule.EvenOdd:
                return FillRule.EvenOdd;
            case System.Windows.Media.FillRule.Nonzero:
                return FillRule.Nonzero;
            default:
                throw new NotSupportedException(string.Format("Not supported fill rule: {0}", fillRule));
        }
    }

    public static GeometryBase ConvertGeometry(Geometry geometry)
    {
        if (geometry is System.Windows.Media.PathGeometry pathGeometry)
        {
            return ConvertPathGeometry(pathGeometry);
        }

        if (geometry is System.Windows.Media.RectangleGeometry rectangleGeometry)
        {
            return ConvertRectangleGeometry(rectangleGeometry);
        }

        if (geometry is EllipseGeometry ellipseGeometry)
        {
            return ConvertEllipseGeometry(ellipseGeometry);
        }

        if (geometry is StreamGeometry streamGeometry)
        {
            return ConvertStreamGeometry(streamGeometry);
        }

        return null;
    }

    public static PathGeometry ConvertPathGeometry(System.Windows.Media.PathGeometry pathGeometry)
    {
        var pdfPathGeometry = new PathGeometry();
        pdfPathGeometry.FillRule = ConvertFillRule(pathGeometry.FillRule);

        foreach (var figure in pathGeometry.Figures)
        {
            pdfPathGeometry.Figures.Add(ConvertPathFigure(figure));
        }

        return pdfPathGeometry;
    }

    public static PathGeometry ConvertStreamGeometry(StreamGeometry streamGeometry)
    {
        var pathGeometry = streamGeometry.GetFlattenedPathGeometry();
        return ConvertPathGeometry(pathGeometry);
    }

    public static RectangleGeometry ConvertRectangleGeometry(System.Windows.Media.RectangleGeometry rectangleGeometry)
    {
        return new RectangleGeometry(rectangleGeometry.Rect);
    }

    public static PathGeometry ConvertEllipseGeometry(EllipseGeometry ellipseGeometry)
    {
        var pathFigure = new PathFigure();
        pathFigure.StartPoint = new Point(ellipseGeometry.RadiusX, 0);

        var arcSegment = new ArcSegment();
        arcSegment.Point = pathFigure.StartPoint;
        arcSegment.RotationAngle = 180;
        arcSegment.RadiusX = ellipseGeometry.RadiusX;
        arcSegment.RadiusY = ellipseGeometry.RadiusY;
        pathFigure.Segments.Add(arcSegment);

        arcSegment = new ArcSegment();
        arcSegment.Point = new Point(ellipseGeometry.RadiusX, 2 * ellipseGeometry.RadiusY);
        arcSegment.RotationAngle = 180;
        arcSegment.RadiusX = ellipseGeometry.RadiusX;
        arcSegment.RadiusY = ellipseGeometry.RadiusY;
        pathFigure.Segments.Add(arcSegment);

        var pathGeometry = new PathGeometry();
        pathFigure.StartPoint = arcSegment.Point;
        pathGeometry.Figures.Add(pathFigure);

        return pathGeometry;
    }

    public static PathFigure ConvertPathFigure(System.Windows.Media.PathFigure pathFigure)
    {
        var pdfFigure = new PathFigure();
        pdfFigure.IsClosed = pathFigure.IsClosed;
        pdfFigure.StartPoint = pathFigure.StartPoint;

        foreach (var segment in pathFigure.Segments)
        {
            foreach (var pdfSegment in ConvertPathSegments(segment))
            {
                pdfFigure.Segments.Add(pdfSegment);
            }
        }

        return pdfFigure;
    }

    public static IEnumerable<Telerik.Windows.Documents.Fixed.Model.Graphics.PathSegment> ConvertPathSegments(
        PathSegment pathSegment)
    {
        var segmentType = pathSegment.GetType();
        Func<PathSegment, IEnumerable<Telerik.Windows.Documents.Fixed.Model.Graphics.PathSegment>> converter;

        if (!segmentConverters.TryGetValue(segmentType, out converter))
        {
            throw new NotSupportedException(string.Format("Not supported PathSegment type: {0}", segmentType));
        }

        return converter(pathSegment);
    }

    public static SweepDirection ConvertSweepDirection(System.Windows.Media.SweepDirection sweepDirection)
    {
        return sweepDirection == System.Windows.Media.SweepDirection.Clockwise
            ? SweepDirection.Clockwise
            : SweepDirection.Counterclockwise;
    }

    public static ArcSegment ConvertArcSegment(System.Windows.Media.ArcSegment arcSegment)
    {
        var pdfArcSegment = new ArcSegment();
        pdfArcSegment.IsLargeArc = arcSegment.IsLargeArc;
        pdfArcSegment.SweepDirection = ConvertSweepDirection(arcSegment.SweepDirection);
        pdfArcSegment.RotationAngle = arcSegment.RotationAngle;
        pdfArcSegment.RadiusX = arcSegment.Size.Width;
        pdfArcSegment.RadiusY = arcSegment.Size.Height;
        pdfArcSegment.Point = arcSegment.Point;

        return pdfArcSegment;
    }

    public static BezierSegment ConvertBezierSegment(System.Windows.Media.BezierSegment bezierSegment)
    {
        var pdfBezierSegment = new BezierSegment();
        pdfBezierSegment.Point1 = bezierSegment.Point1;
        pdfBezierSegment.Point2 = bezierSegment.Point2;
        pdfBezierSegment.Point3 = bezierSegment.Point3;

        return pdfBezierSegment;
    }

    public static IEnumerable<BezierSegment> ConvertPolyBezierSegments(PolyBezierSegment polyBezierSegment)
    {
        var points = polyBezierSegment.Points;

        for (var index = 2; index < points.Count; index += 3)
        {
            var pdfBezierSegment = new BezierSegment();
            pdfBezierSegment.Point1 = points[index - 2];
            pdfBezierSegment.Point2 = points[index - 1];
            pdfBezierSegment.Point3 = points[index];

            yield return pdfBezierSegment;
        }
    }

    public static LineSegment ConvertLineSegment(System.Windows.Media.LineSegment lineSegment)
    {
        var pdfLineSegment = new LineSegment();
        pdfLineSegment.Point = lineSegment.Point;

        return pdfLineSegment;
    }

    public static IEnumerable<LineSegment> ConvertPolyLineSegment(PolyLineSegment polyLineSegment)
    {
        foreach (var point in polyLineSegment.Points)
        {
            var pdfLineSegment = new LineSegment();
            pdfLineSegment.Point = point;

            yield return pdfLineSegment;
        }
    }

    public static QuadraticBezierSegment ConvertQuadraticBezierSegment(
        System.Windows.Media.QuadraticBezierSegment quadraticBezierSegment)
    {
        var pdfQuadraticBezierSegment = new QuadraticBezierSegment();
        pdfQuadraticBezierSegment.Point1 = quadraticBezierSegment.Point1;
        pdfQuadraticBezierSegment.Point2 = quadraticBezierSegment.Point2;

        return pdfQuadraticBezierSegment;
    }

    public static IEnumerable<QuadraticBezierSegment> ConvertPolyQuadraticBezierSegment(
        PolyQuadraticBezierSegment polyQuadraticBezierSegment)
    {
        var points = polyQuadraticBezierSegment.Points;

        for (var index = 1; index < points.Count; index += 2)
        {
            var pdfQuadraticBezierSegment = new QuadraticBezierSegment();
            pdfQuadraticBezierSegment.Point1 = points[index - 1];
            pdfQuadraticBezierSegment.Point2 = points[index];

            yield return pdfQuadraticBezierSegment;
        }
    }

    private static IEnumerable<ArcSegment> ToArcSegments(System.Windows.Media.ArcSegment arcSegment)
    {
        yield return ConvertArcSegment(arcSegment);
    }

    private static IEnumerable<BezierSegment> ToBezierSegments(System.Windows.Media.BezierSegment bezierSegment)
    {
        yield return ConvertBezierSegment(bezierSegment);
    }

    private static IEnumerable<LineSegment> ToLineSegments(System.Windows.Media.LineSegment lineSegment)
    {
        yield return ConvertLineSegment(lineSegment);
    }

    private static IEnumerable<QuadraticBezierSegment> ToQuadraticBezierSegments(
        System.Windows.Media.QuadraticBezierSegment quadraticBezierSegment)
    {
        yield return ConvertQuadraticBezierSegment(quadraticBezierSegment);
    }
}